﻿#include "fixq.h"
#include "debug.h"

/**
 * @brief 定长消息队列初始化
 * @param q 定长消息队列
 * @param buf 指向存放消息数组的地址
 * @param size 定长消息队列最大长度
 * @param type 锁的类型
 */
net_err_t fixq_init(fixq_t *q, void **buf, int size, nlocker_type_t type)
{
    if (!q || !buf)
    {
        DBG_ERROR(DBG_FIXQ_LEVEL, "q or buf is null!");
        return NET_ERR_SYS;
    }

    q->size = size;
    q->in = q->out = q->cnt = 0;
    q->buf = buf;
    q->send_sem = q->recv_sem = SYS_SEM_INVALID;

    if (nlocker_init(&q->locker, type) < NET_ERR_OK)
    {
        DBG_WARNING(DBG_FIXQ_LEVEL, "init  nlocker failed!");
        return NET_ERR_SYS;
    }

    q->recv_sem = sys_sem_create(0);
    if (SYS_SEM_INVALID == q->recv_sem)
    {
        DBG_WARNING(DBG_FIXQ_LEVEL, "create recv_sem failed!");
        goto init_failed;
    }

    q->send_sem = sys_sem_create(size);
    if (SYS_SEM_INVALID == q->send_sem)
    {
        DBG_WARNING(DBG_FIXQ_LEVEL, "create send_sem failed!");
        goto init_failed;
    }

    return NET_ERR_OK;

init_failed:
    nlocker_destroy(&q->locker);
    if (SYS_SEM_INVALID != q->recv_sem)
    {
        sys_sem_free(q->recv_sem);
    }
    if (SYS_SEM_INVALID != q->send_sem)
    {
        sys_sem_free(q->send_sem);
    }

    return NET_ERR_SYS;
}

/**
 * @brief 向指定的消息队列中发送消息
 * @param q 指定的消息队列地址
 * @param msg 需要发送的消息
 * @param tmo 超时等待时间
 */
net_err_t fixq_send(fixq_t *q, void *msg, int tmo)
{
    if (!q || !msg)
        return NET_ERR_NULLP;

    // 若等待时间小于0，先判断消息队列中是否已满
    nlocker_lock(&q->locker);
    if (tmo < 0 && q->cnt >= q->size)
    {
        DBG_INFO(DBG_FIXQ_LEVEL, "msg queue is full, no free msg!");
        nlocker_unlock(&q->locker);
        return NET_ERR_FULL;
    }
    nlocker_unlock(&q->locker);

    // 等待并发送消息
    if (sys_sem_wait(q->send_sem, tmo) < 0)
    {
        DBG_INFO(DBG_FIXQ_LEVEL, "wait send time out!");
        return NET_ERR_TIMEOUT;
    }
    nlocker_lock(&q->locker);
    q->buf[q->in++] = msg;
    if (q->in >= q->size)
    {
        q->in = 0;
    }
    ++q->cnt;
    nlocker_unlock(&q->locker);

    // 告知接受信号量
    sys_sem_notify(q->recv_sem);
    return NET_ERR_OK;
}

/**
 * @brief 从指定的消息队列中接受消息
 * @param q 指定的消息队列
 * @param tmo 超时等待时间
 */
void *fixq_recv(fixq_t *q, int tmo)
{
    if (!q)
    {
        return NULL;
    }

    // 若无需等待，检查消息数量
    nlocker_lock(&q->locker);
    if (tmo < 0 && !q->cnt)
    {
        // DBG_INFO(DBG_FIXQ_LEVEL, "no msg in the fixq!");
        nlocker_unlock(&q->locker);
        return NULL;
    }
    nlocker_unlock(&q->locker);

    // 等待并接受消息
    if (sys_sem_wait(q->recv_sem, tmo) < 0)
    {
        DBG_INFO(DBG_FIXQ_LEVEL, "wait recv time out!");
        return NULL;
    }
    nlocker_lock(&q->locker);
    void *msg = q->buf[q->out++];
    if (q->out >= q->size)
    {
        q->out = 0;
    }
    --q->cnt;
    nlocker_unlock(&q->locker);

    // 告知发送信号量
    sys_sem_notify(q->send_sem);
    return msg;
}

void fixq_destroy(fixq_t *q)
{
    nlocker_lock(&q->locker);
    sys_sem_free(q->recv_sem);
    sys_sem_free(q->send_sem);
    nlocker_unlock(&q->locker);
    nlocker_destroy(&q->locker);
}

int fixq_count(const fixq_t *q)
{
    return q->cnt;
}